﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using TeamFoundation.EventBus.Client.Properties;

namespace TeamFoundation.EventBus.Client
{
    /// <summary>
    /// Manages subscriptions on the server.
    /// </summary>
    public static class SubscriptionManager
    {
        internal static string GetManagerUri(Uri host)
        {
            string hst = host.ToString();
            if (!hst.EndsWith("/"))
            {
                host = new Uri(hst + "/");
            }
            Uri part = new Uri("./Management.asmx", UriKind.Relative);
            return new Uri(host, part).ToString();
        }

        /// <summary>
        /// Gets a specific subscription.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="name">The name of the subscription.</param>
        /// <returns>The <see cref="Subscription"/> with the given <see cref="Subscription.Name"/>.</returns>
        public static Subscription Get(Uri uri, string name)
        {
            return Get(uri, CredentialCache.DefaultNetworkCredentials, name);
        }

        /// <summary>
        /// Gets a specific subscription.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="name">The name of the subscription.</param>
        /// <param name="credentials">The credentials to use during the transaction.</param>
        /// <returns>The <see cref="Subscription"/> with the given <see cref="Subscription.Name"/>.</returns>
        public static Subscription Get(Uri uri, ICredentials credentials, string name)
        {
            using (var manager = new Service.Management(GetManagerUri(uri).ToString()))
            {
                manager.Credentials = credentials;

                var sub = manager.GetSubscription(name);

                if (sub == null)
                    return null;

                return new Subscription(sub);
            }
        }

        /// <summary>
        /// Updates or creates a specific subscription.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="subscription">The subscription to update.</param>
        /// <returns>The <see cref="Subscription"/> with the given <see cref="Subscription.Name"/>.</returns>
        public static Subscription Update(Uri uri, Subscription subscription)
        {
            return Update(uri, CredentialCache.DefaultNetworkCredentials, subscription);
        }

        /// <summary>
        /// Updates or creates a specific subscription.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="name">The name of the subscription.</param>
        /// <param name="credentials">The credentials to use during the transaction.</param>
        /// <returns>The <see cref="Subscription"/> with the given <see cref="Subscription.Name"/>.</returns>
        public static Subscription Update(Uri uri, ICredentials credentials, Subscription subscription)
        {
            if (subscription.Filter == null)
                subscription.Filter = string.Empty;

            using (var manager = new Service.Management(GetManagerUri(uri).ToString()))
            {
                manager.Credentials = credentials;

                var sub = new Service.EventSubscription();
                sub.filter = subscription.Filter;
                sub.name = subscription.Name;
                sub.pipe = subscription.Queue;
                manager.Update(ref sub);

                if (sub == null)
                    return null;

                return new Subscription(sub);
            }
        }


        /// <summary>
        /// Creates a new subscription.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="name">The name of the subscription.</param>
        /// <param name="queue">The queue that should be created.</param>
        /// <param name="filter">The filter XPath.</param>
        /// <returns>The new <see cref="Subscription"/>.</returns>
        public static Subscription Create(Uri uri, string name, string queue, string filter)
        {
            return Create(uri, CredentialCache.DefaultNetworkCredentials, name, queue, filter);
        }

        /// <summary>
        /// Creates a new subscription.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="name">The name of the subscription.</param>
        /// <param name="queue">The queue that should be created.</param>
        /// <param name="filter">The filter XPath.</param>
        /// <param name="credentials">The credentials to use during the transaction.</param>
        /// <returns>The new <see cref="Subscription"/>.</returns>
        public static Subscription Create(Uri uri, ICredentials credentials, string name, string queue, string filter)
        {
            if (filter == null)
                filter = string.Empty;

            using (var manager = new Service.Management(GetManagerUri(uri).ToString()))
            {
                manager.Credentials = credentials;

                var sub = new Service.EventSubscription();
                sub.filter = filter;
                sub.name = name;
                sub.pipe = queue;

                if (manager.GetSubscription(name) != null)
                    throw new InvalidOperationException(Resources.SubExists);

                manager.Update(ref sub);

                if (sub == null)
                    return null;

                return new Subscription(sub);
            }
        }

        /// <summary>
        /// Creates a new subscription.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="name">The name of the subscription.</param>
        /// <param name="queue">The queue that should be created.</param>
        /// <param name="filter">The filter XPath.</param>
        /// <returns>The new <see cref="Subscription"/>.</returns>
        public static Subscription CreateOrGet(Uri uri, string name, string queue, string filter)
        {
            return CreateOrGet(uri, CredentialCache.DefaultNetworkCredentials, name, queue, filter);
        }

        /// <summary>
        /// Creates a new subscription.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="name">The name of the subscription.</param>
        /// <param name="queue">The queue that should be created.</param>
        /// <param name="filter">The filter XPath.</param>
        /// <param name="credentials">The credentials to use during the transaction.</param>
        /// <returns>The new <see cref="Subscription"/>.</returns>
        public static Subscription CreateOrGet(Uri uri, ICredentials credentials, string name, string queue, string filter)
        {
            if (filter == null)
                filter = string.Empty;

            using (var manager = new Service.Management(GetManagerUri(uri).ToString()))
            {
                manager.Credentials = credentials;

                var osub = manager.GetSubscription(name);
                if(osub != null)
                {
                    return new Subscription(osub);
                }

                var sub = new Service.EventSubscription();
                sub.filter = filter;
                sub.name = name;
                sub.pipe = queue;

                manager.Update(ref sub);

                if (sub == null)
                    return null;

                return new Subscription(sub);
            }
        }

        /// <summary>
        /// Gets a list of all the subscriptions.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <returns>A list of <see cref="Subscription"/>s.</returns>
        public static IEnumerable<Subscription> GetAll(Uri uri)
        {
            return GetAll(uri, CredentialCache.DefaultNetworkCredentials);
        }

        /// <summary>
        /// Gets a list of all the subscriptions.
        /// </summary>
        /// <param name="uri">The URL to the manager service.</param>
        /// <param name="credentials">The credentials to use during the transaction.</param>
        /// <returns>A list of <see cref="Subscription"/>s.</returns>
        public static IEnumerable<Subscription> GetAll(Uri uri, ICredentials credentials)
        {
            using (var manager = new Service.Management(GetManagerUri(uri).ToString()))
            {
                manager.Url = GetManagerUri(uri).ToString();
                manager.Credentials = credentials;

                var subs = manager.GetAllSubscriptions();
                foreach (var sub in subs)
                {
                    yield return new Subscription(sub);
                }
            }
        }
    }
}
